<script>
import { defineComponent } from 'vue';
import { createNamespace } from '../utils';

export default defineComponent({
  name: createNamespace('PullRefresh')[0],
  options: { virtualHost: true },
});
</script>

<script setup>
import { reactive, computed, getCurrentInstance } from 'vue';
import { getElementBounding } from '../utils';
import { usePageLifecycle } from '../hooks';
import FanLoading from '../loading/loading.vue';

const emit = defineEmits(['refresh']);
const props = defineProps({
  pullingText: {
    type: String,
    default: '下拉即可刷新...',
  },
  loosingText: {
    type: String,
    default: '释放即可刷新...',
  },
  loadingText: {
    type: String,
    default: '加载中...',
  },
  successText: {
    type: String,
    default: '加载成功',
  },
  successDuration: {
    type: [Number, String],
    default: 500,
  },
});

const { onPageScroll } = usePageLifecycle();
const state = reactive({
  status: 'success', // loading pulling loosing success
  height: 50,
  top: 0,
  isTouch: false,
  isPullingGesture: false,
  start: 0,
  startY: 0,
  isTop: true,
});

const statusTxt = computed(() => {
  let txt = '';
  if (state.status === 'pulling') {
    txt = props.pullingText;
  } else if (state.status === 'loosing') {
    txt = props.loosingText;
  } else if (state.status === 'loading') {
    txt = props.loadingText;
  } else {
    txt = props.successText;
  }
  return txt;
});

const _this = getCurrentInstance();
const getEl = async (el) => {
  const ret = await getElementBounding({
    selector: el,
    scope: _this,
  });
  if (!ret) return {};
  return ret;
};

const ease = (move) => {
  move = move - state.start;
  if (move > state.height) {
    if (move < state.height * 2) {
      move = state.height + (move - state.height) / 2;
    } else {
      move = state.height * 1.5 + (move - state.height * 2) / 4;
    }
  }
  return Math.round(move);
};

// 手指按下
const onTouchStart = async (e) => {
  const { top } = await getEl('.fan-pull-refresh__head');
  state.isTouch = true;
  state.start = e.touches[0].clientY;
  state.startY = top;
};

// 下拉
const onTouchMove = async (e) => {
  // 不在顶部且未处于刷新：放行上滑
  if (!state.isTop && state.status === 'success') {
    state.isPullingGesture = false;
    return;
  }
  const delta = e.touches[0].clientY - state.start;
  if (delta > 0) {
    // 向下拉动：进入下拉刷新手势
    state.isPullingGesture = true;
    const move = ease(e.touches[0].clientY);
    state.top = move > 0 ? move : 0;
    onScroll();
    // 仅在下拉时阻止冒泡，避免外层滚动
    e.stopPropagation();
  } else {
    // 向上滑动：允许页面滚动
    state.isPullingGesture = false;
  }
};

const stopPullRefresh = () => {
  state.status = 'success';
  setTimeout(() => {
    state.top = 0;
  }, Number(props.successDuration));
};

// 手指离开
const onTouchEnd = () => {
  if (state.top >= state.height) {
    state.status = 'loading';
    state.top = state.height;
    emit('refresh', stopPullRefresh);
  } else {
    state.top = 0;
  }
  state.isTouch = false;
  state.isPullingGesture = false;
};

const onScroll = () => {
  const move = Math.round(state.top - state.startY);
  if (move >= state.height) {
    state.status = 'loosing';
  } else {
    state.status = 'pulling';
  }
};

onPageScroll(({ scrollTop }) => {
  if (scrollTop > 0) {
    state.isTop = false;
  } else {
    state.isTop = true;
  }
});
</script>

<template>
  <div
    class="fan-pull-refresh"
    :style="{
      touchAction: state.isPullingGesture ? 'none' : 'pan-y',
    }"
    @touchstart.passive="onTouchStart"
    @touchmove.passive="onTouchMove"
    @touchend="onTouchEnd"
    @touchcancel="onTouchEnd"
  >
    <div
      class="fan-pull-refresh__inner"
      :style="{
        transform: `translate3d(0, ${state.top}px, 0)`,
        transitionDuration: state.isTouch ? '0ms' : '300ms',
      }"
    >
      <div
        class="fan-pull-refresh__head fan-flex-center"
        :style="{ height: `${state.height}px` }"
      >
        <slot v-if="state.status === 'loading'" name="loading">
          <FanLoading size="16" style="margin-right: 6px" />
          {{ statusTxt }}
        </slot>
        <slot v-else-if="state.status === 'pulling'" name="pulling">{{
          statusTxt
        }}</slot>
        <slot v-else-if="state.status === 'loosing'" name="loosing">{{
          statusTxt
        }}</slot>
        <slot v-else name="success">{{ statusTxt }}</slot>
      </div>
      <slot />
    </div>
  </div>
</template>

<style lang="less">
.fan-pull-refresh {
  overflow: hidden;
  &__inner {
    height: 100%;
    transition-property: transform;
    position: relative;
  }
  &__head {
    position: absolute;
    left: 0;
    width: 100%;
    overflow: hidden;
    color: #969799;
    font-size: 14px;
    transform: translateY(-100%);
  }
}
</style>
